﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace AcademicPlanner.Presentation
{
	/// <summary>
	/// FormSubmission encapsulates POST data and provides convenient ways to read it.
	/// POST data can be seen as partitioned using a common prefix to names.
	/// A <code>FormSubmission</code> object represents a single such partition.
	/// See <see cref="T:Form"/> for more on how form partitioning is designed.
	/// </summary>
	/// <seealso cref="T:Form"/>
	/// <since>1.0</since>
	public class FormSubmission
	{
		NameValueCollection values;

		Dictionary<string, FormSubmission> subs = new Dictionary<string, FormSubmission>();


		/// <summary>
		/// Gets or sets the prefix of POST data partition names this FormSubmission represents.
		/// </summary>
		/// <since>1.0</since>
		public string NamesPrefix { get; set; }

		/// <summary>
		/// Gets the anti-forgery token sent along with the POST request (or an empty string);
		/// it is stored under the name "__signature".
		/// </summary>
		/// <since>1.0</since>
		public string Signature { get { return (values["__signature"] ?? ""); } }


		/// <summary>
		/// Sole constructor; sets the <c>NameValueCollection</c> containing the POST data.
		/// </summary>
		/// <param name="pValues">The <c>NameValueCollection</c> containing the POST data.</param>
		/// <since>1.0</since>
		public FormSubmission(NameValueCollection pValues)
		{
			values = pValues;
		}


		/// <summary>
		/// Creates and adds another <c>FormSubmission</c> under this FormSubmission.
		/// This method sets the <c>NamesPrefix</c> of the created <c>FormSubmission</c>
		/// to the complete name (returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>)
		/// of the given name under this FormSubmission.
		/// </summary>
		/// <param name="pName">The name of <c>FormSubmission</c> to create and add.</param>
		/// <returns>The <c>FormSubmission</c> created and added.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, int, string, int)"/>
		/// <since>1.0</since>
		public FormSubmission AddSubFormSubmission(string pName)
		{
			var vSub = new FormSubmission(values);
			
			vSub.NamesPrefix = Form.GetFieldName(NamesPrefix, pName, null, null, null);
			subs[pName] = vSub;

			return vSub;
		}

		/// <summary>
		/// Gets a <c>FormSubmission</c> under this FormSubmission.
		/// </summary>
		/// <param name="pName">The name of <c>FormSubmission</c>.</param>
		/// <returns>The <c>FormSubmission</c> under this FormSubmission or <c>null</c>.</returns>
		/// <since>1.0</since>
		public FormSubmission GetSubFormSubmission(string pName)
		{
			return subs[pName];
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, string)"/>
		/// with the second parameter <c>""</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <seealso cref="M:GetAsString(string, string)"/>
		/// <since>1.0</since>
		public string GetAsString(string pName)
		{
			return GetAsString(pName, "");
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, int, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <seealso cref="M:GetAsString(string, int, string)"/>
		/// <since>1.0</since>
		public string GetAsString(string pName, string pParent)
		{
			return GetAsString(pName, null, pParent);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>string</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, int, string, int)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <since>1.0</since>
		public string GetAsString(string pName, string pParent, int? pParentIndex)
		{
			return GetAsString(pName, null, pParent, pParentIndex);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>string</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, int, string)"/>
		/// with the third parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it is a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <since>1.0</since>
		public string GetAsString(string pName, int? pIndex)
		{
			return GetAsString(pName, pIndex, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>string</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, int, string, int)"/>
		/// with the second and fourth parameters <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <since>1.0</since>
		public string GetAsString(string pName, int? pIndex, string pParent)
		{
			return GetAsString(pName, null, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>string</c>.
		/// </summary>
		/// <remarks>
		/// This method considers that the field's value is stored in the POST data collection 
		/// keyed by the field's complete name as returned by 
		/// <see cref="M:Form.GetFieldName(string, string, int, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <since>1.0</since>
		public string GetAsString(string pName, int? pIndex, string pParent, int? pParentIndex)
		{
			string rVal = values[Form.GetFieldName(NamesPrefix, pName, pIndex, pParent, pParentIndex)];
			return rVal ?? "";
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>string</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsStrings(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <returns>The field's values as <c>string</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, string> GetAsStrings(string pName)
		{
			return GetAsStrings(pName, null);
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>string</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsStrings(string, string, bool)"/>
		/// with the third parameter <c>false</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <returns>The field's values as <c>string</c>s.</returns>
		public IDictionary<int, string> GetAsStrings(string pName, string pParent)
		{
			return GetAsStrings(pName, pParent, false);
		}

		/// <summary>
		/// Gets POST data values of a field that either it or its parent
		/// is a collection as <c>string</c>s.
		/// </summary>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <param name="pParentIsTheCollection">
		/// Whether the field's parent is the collection or the field is.
		/// </param>
		/// <returns>The field's values as <c>string</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, string> GetAsStrings(string pName, string pParent, bool pParentIsTheCollection)
		{
			string vVal;
			string vIndexStr;
			string vSampleName;
			int vIndex;
			
			if (pParentIsTheCollection)
				vSampleName = Form.GetFieldName(NamesPrefix, pName, null, pParent, -1);
			else
				vSampleName = Form.GetFieldName(NamesPrefix, pName, -1, pParent, null);


			var rVals = new Dictionary<int, string>();
			var vNameBeg = vSampleName.Substring(0, vSampleName.IndexOf("-1"));
			var vKeys = from vKey in values.AllKeys
						where !string.IsNullOrEmpty(vKey) && vKey.StartsWith(vNameBeg) && vKey.EndsWith(pName)
						select vKey;

			foreach (var vKey in vKeys)
			{
				vVal = values[vKey];
				vIndexStr = vKey.Replace(pName, string.Empty);
				if (!string.IsNullOrEmpty(NamesPrefix))
					vIndexStr = vIndexStr.Replace(NamesPrefix, string.Empty);
				if (!string.IsNullOrEmpty(pParent))
					vIndexStr = vIndexStr.Replace(pParent, string.Empty);
				vIndexStr = vIndexStr.Replace(Form.Separator, string.Empty);
				if (int.TryParse(vIndexStr, out vIndex))
				{
					rVals.Add(vIndex, vVal);
				}
			}

			return rVals;
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, string)"/>
		/// with the second parameter <c>""</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <seealso cref="M:GetAsNumber(string, string)"/>
		/// <since>1.0</since>
		public double GetAsNumber(string pName)
		{
			return GetAsNumber(pName, "");
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, int, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, string pParent)
		{
			return GetAsNumber(pName, null, pParent);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>double</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, int, string, int)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, string pParent, int? pParentIndex)
		{
			return GetAsNumber(pName, null, pParent, pParentIndex);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>double</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, int, string)"/>
		/// with the third parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it is a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, int? pIndex)
		{
			return GetAsNumber(pName, pIndex, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>double</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, int, string, int)"/>
		/// with the second and fourth parameters <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, int? pIndex, string pParent)
		{
			return GetAsNumber(pName, null, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>double</c>.
		/// </summary>
		/// <remarks>
		/// This method considers that the field's value is stored in the POST data collection 
		/// keyed by the field's complete name as returned by 
		/// <see cref="M:Form.GetFieldName(string, string, int, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, int? pIndex, string pParent, int? pParentIndex)
		{
			double rVal = double.MinValue;
			string vVal = values[Form.GetFieldName(NamesPrefix, pName, pIndex, pParent, pParentIndex)];

			if (vVal != null)
			{
				if (!double.TryParse(vVal, out rVal))
					rVal = double.MinValue;
			}

			return rVal; 
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>double</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsNumbers(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <returns>The field's values as <c>double</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, double> GetAsNumbers(string pName)
		{
			return GetAsNumbers(pName, null);
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>double</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsNumbers(string, string, bool)"/>
		/// with the third parameter <c>false</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <returns>The field's values as <c>double</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, double> GetAsNumbers(string pName, string pParent)
		{
			return GetAsNumbers(pName, pParent, false);
		}

		/// <summary>
		/// Gets POST data values of a field that either it or its parent
		/// is a collection as <c>double</c>s.
		/// </summary>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <param name="pParentIsTheCollection">
		/// Whether the field's parent is the collection or the field is.
		/// </param>
		/// <returns>The field's values as <c>double</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, double> GetAsNumbers(string pName, string pParent, bool pParentIsTheCollection)
		{
			string vStrVal;
			string vIndexStr;
			int vIndex;
			double vVal;
			string vSampleName;

			if (pParentIsTheCollection)
				vSampleName = Form.GetFieldName(NamesPrefix, pName, null, pParent, -1);
			else
				vSampleName = Form.GetFieldName(NamesPrefix, pName, -1, pParent, null);

			var rVals = new Dictionary<int, double>();
			var vNameBeg = vSampleName.Substring(0, vSampleName.IndexOf("-1"));
			var vKeys = from vKey in values.AllKeys
						where !string.IsNullOrEmpty(vKey) && vKey.StartsWith(vNameBeg) && vKey.EndsWith(pName)
						select vKey;

			foreach (var vKey in vKeys)
			{
				vStrVal = values[vKey]; vIndexStr = vKey.Replace(pName, string.Empty);
				if (!string.IsNullOrEmpty(NamesPrefix))
					vIndexStr = vIndexStr.Replace(NamesPrefix, string.Empty);
				if (!string.IsNullOrEmpty(pParent))
					vIndexStr = vIndexStr.Replace(pParent, string.Empty);
				vIndexStr = vIndexStr.Replace(Form.Separator, string.Empty);
				if (int.TryParse(vIndexStr, out vIndex) && double.TryParse(vStrVal, out vVal))
				{
					rVals.Add(vIndex, vVal);
				}
			}

			return rVals;
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, string)"/>
		/// with the second parameter <c>""</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <seealso cref="M:GetAsInteger(string, string)"/>
		/// <since>1.0</since>
		public int GetAsInteger(string pName)
		{
			return GetAsInteger(pName, "");
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, int, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, string pParent)
		{
			return GetAsInteger(pName, null, pParent);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>int</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, int, string, int)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, string pParent, int? pParentIndex)
		{
			return GetAsInteger(pName, null, pParent, pParentIndex);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>int</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, int, string)"/>
		/// with the third parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it is a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, int? pIndex)
		{
			return GetAsInteger(pName, pIndex, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>int</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, int, string, int)"/>
		/// with the second and fourth parameters <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, int? pIndex, string pParent)
		{
			return GetAsInteger(pName, null, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>int</c>.
		/// </summary>
		/// <remarks>
		/// This method considers that the field's value is stored in the POST data collection 
		/// keyed by the field's complete name as returned by 
		/// <see cref="M:Form.GetFieldName(string, string, int, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, int? pIndex, string pParent, int? pParentIndex)
		{
			int rVal = int.MinValue;
			string vVal = values[Form.GetFieldName(NamesPrefix, pName, pIndex, pParent, pParentIndex)];

			if (vVal != null)
			{
				if (!int.TryParse(vVal, out rVal))
					rVal = int.MinValue;
			}

			return rVal; 
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>int</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsIntegers(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <returns>The field's values as <c>int</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, int> GetAsIntegers(string pName)
		{
			return GetAsIntegers(pName, null);
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>int</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsIntegers(string, string, bool)"/>
		/// with the third parameter <c>false</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <returns>The field's values as <c>int</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, int> GetAsIntegers(string pName, string pParent)
		{
			return GetAsIntegers(pName, pParent, false);
		}

		/// <summary>
		/// Gets POST data values of a field that either it or its parent
		/// is a collection as <c>int</c>s.
		/// </summary>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <param name="pParentIsTheCollection">
		/// Whether the field's parent is the collection or the field is.
		/// </param>
		/// <returns>The field's values as <c>int</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, int> GetAsIntegers(string pName, string pParent, bool pParentIsTheCollection)
		{
			string vStrVal;
			string vIndexStr;
			int vIndex;
			int vVal;
			string vSampleName;

			if (pParentIsTheCollection)
				vSampleName = Form.GetFieldName(NamesPrefix, pName, null, pParent, -1);
			else
				vSampleName = Form.GetFieldName(NamesPrefix, pName, -1, pParent, null);

			var rVals = new Dictionary<int, int>();
			var vNameBeg = vSampleName.Substring(0, vSampleName.IndexOf("-1"));
			var vKeys = from vKey in values.AllKeys
						where !string.IsNullOrEmpty(vKey) && vKey.StartsWith(vNameBeg) && vKey.EndsWith(pName)
						select vKey;

			foreach (var vKey in vKeys)
			{
				vStrVal = values[vKey];
				vIndexStr = vKey.Replace(pName, string.Empty);
				if (!string.IsNullOrEmpty(NamesPrefix))
					vIndexStr = vIndexStr.Replace(NamesPrefix, string.Empty);
				if (!string.IsNullOrEmpty(pParent))
					vIndexStr = vIndexStr.Replace(pParent, string.Empty);
				vIndexStr = vIndexStr.Replace(Form.Separator, string.Empty);
				if (int.TryParse(vIndexStr, out vIndex) && int.TryParse(vStrVal, out vVal))
				{
					rVals.Add(vIndex, vVal);
				}
			}

			return rVals;
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, string)"/>
		/// with the second parameter <c>""</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>bool</c> or <c>false</c>.</returns>
		/// <seealso cref="M:GetAsBoolean(string, string)"/>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName)
		{
			return GetAsBoolean(pName, "");
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, int, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>bool</c> or <c>false</c>.</returns>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, string pParent)
		{
			return GetAsBoolean(pName, null, pParent);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>bool</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, int, string, int)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>bool</c> or <c>true</c>.</returns>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, string pParent, int? pParentIndex)
		{
			return GetAsBoolean(pName, null, pParent, pParentIndex);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>bool</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, int, string)"/>
		/// with the third parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it is a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>bool</c> or <c>true</c>.</returns>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, int? pIndex)
		{
			return GetAsBoolean(pName, pIndex, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>bool</c>.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, int, string, int)"/>
		/// with the second and fourth parameters <c>null</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>bool</c> or <c>true</c>.</returns>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, int? pIndex, string pParent)
		{
			return GetAsBoolean(pName, null, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>bool</c>.
		/// </summary>
		/// <remarks>
		/// This method considers that the field's value is stored in the POST data collection 
		/// keyed by the field's complete name as returned by 
		/// <see cref="M:Form.GetFieldName(string, string, int, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </remarks>
		/// <param name="pName">The field's name</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pParentIndex">
		/// The index of the field's parent if the parent is a collection; may be <c>null</c>.
		/// </param>
		/// <returns>The field's value as <c>bool</c> or <c>true</c>.</returns>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, int? pIndex, string pParent, int? pParentIndex)
		{
			bool rVal = true;
			string vVal = values[Form.GetFieldName(NamesPrefix, pName, pIndex, pParent, pParentIndex)];

			if (vVal != null)
				if (!bool.TryParse(vVal, out rVal))
				rVal = true;

			return rVal;
		}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>bool</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsBooleans(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <returns>The field's values as <c>bool</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, bool> GetAsBooleans(string pName)
		{
			return GetAsBooleans(pName, null);
	}

		/// <summary>
		/// Gets POST data values of a field that is a collection as <c>bool</c>s.
		/// </summary>
		/// <remarks>
		/// This method is a convenience overload of <see cref="M:GetAsBooleans(string, string, bool)"/>
		/// with the third parameter <c>false</c>.
		/// </remarks>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <returns>The field's values as <c>bool</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, bool> GetAsBooleans(string pName, string pParent)
		{
			return GetAsBooleans(pName, pParent, false);
}

		/// <summary>
		/// Gets POST data values of a field that either it or its parent
		/// is a collection as <c>bool</c>s.
		/// </summary>
		/// <param name="pName">The name of the field.</param>
		/// <param name="pParent">The name of the field's parent.</param>
		/// <param name="pParentIsTheCollection">
		/// Whether the field's parent is the collection or the field is.
		/// </param>
		/// <returns>The field's values as <c>bool</c>s.</returns>
		/// <since>1.0</since>
		public IDictionary<int, bool> GetAsBooleans(string pName, string pParent, bool pParentIsTheCollection)
		{
			string vStrVal;
			string vIndexStr;
			int vIndex;
			bool vVal;
			string vSampleName;

			if (pParentIsTheCollection)
				vSampleName = Form.GetFieldName(NamesPrefix, pName, null, pParent, -1);
			else
				vSampleName = Form.GetFieldName(NamesPrefix, pName, -1, pParent, null);

			var rVals = new Dictionary<int, bool>();
			var vNameBeg = vSampleName.Substring(0, vSampleName.IndexOf("-1"));
			var vKeys = from vKey in values.AllKeys
						where !string.IsNullOrEmpty(vKey) && vKey.StartsWith(vNameBeg) && vKey.EndsWith(pName)
						select vKey;

			foreach (var vKey in vKeys)
			{
				vStrVal = values[vKey];
				vIndexStr = vKey.Replace(pName, string.Empty);
				if (!string.IsNullOrEmpty(NamesPrefix))
					vIndexStr = vIndexStr.Replace(NamesPrefix, string.Empty);
				if (!string.IsNullOrEmpty(pParent))
					vIndexStr = vIndexStr.Replace(pParent, string.Empty);
				vIndexStr = vIndexStr.Replace(Form.Separator, string.Empty);
				if (int.TryParse(vIndexStr, out vIndex) && bool.TryParse(vStrVal, out vVal))
				{
					rVals.Add(vIndex, vVal);
				}
			}

			return rVals;
		}

	}
}
